/**
 * 
 */
package com.curluin.model;

import java.util.ArrayList;
import java.util.Vector;

import com.curluin.controll.Controller;
import com.curluin.math.MyVector;
import com.curluin.scene.Cube;
import com.curluin.scene.TriangleMesh;

/**
 * @author Alex,Jo
 *
 */
public class GridField {

	private static final MyVector initialDebugColor=TriangleMesh.BLACK;
	private static final MyVector touchedDebugColor=TriangleMesh.RED;
	private static final String LOG_TAG = "GridField";
	/**
	 * decrease of Friction per Touch in percent
	 */
	private static final float frictionDecreasePerTouch = 0.05f;
	/**
	 * maximal decrease of Friction through wipes in percent
	 */
	private static final float maxFrictionDecrease = 0.5f;
	private MyVector actDebugColor=initialDebugColor;

	/**
	 * All modelObject's which are positioned on the GridField except Ground
	 */
	private ArrayList<ModelObject> occupants = new ArrayList<ModelObject>();

	/**
	 * Ground of the GridField
	 */
	//private Ground ground;

	private ArrayList<Ground> grounds = new ArrayList<Ground>();

	/**
	 * Counts how often this GridField was touched. Used for computing friction of this area of ground.
	 */
	private int touchCounter;

	//	private Controller controller;

	/**
	 * Center of GridField in the Visual 3d space
	 */
	private MyVector center;

	/**
	 * With of the quadratic GridField area
	 */
	private float width;

	/**
	 * A cube positioned in the center of the GridField, only for debugging purposes
	 */
	private TriangleMesh debugCube;

	/**
	 * only for debugColor
	 */
	private int numGroundObstacles;

	public GridField(MyVector center) {
		super();
		//		this.controller = controller;
		this.center = center;
		this.width = Controller.getGridFieldWidth();
		debugInitialization();
	}

	private void debugInitialization(){
		if(!Controller.DEBUG_MODE) return;
		this.debugCube = new Cube(center, width*0.8f, 0.3f, width*0.8f, initialDebugColor);
	}

	/**
	 * Adds an occupant who doesn't occupy the GridField already. Possibly "throws events" such as collisions to Controller
	 * @param newOccupant
	 */
	public void addOccupant(ModelObject newOccupant){

		if(occupants.contains(newOccupant)) return;
		if(newOccupant instanceof Moveable){
			Moveable mmo = (Moveable) newOccupant;
			for(ModelObject mo:occupants){
				if(mo instanceof Obstacle){ //checks for collision
					MyVector collisionLoc=((Obstacle)mo).detailledCollisionCheck(newOccupant);
					if(collisionLoc!=null) mmo.addCollision(mo,collisionLoc); 
				}
			}
			mmo.addGridField(this);
		}

		if(newOccupant instanceof Ground){
			Ground ground = (Ground) newOccupant;
			addGround(ground);
			setDebugColor(ground,false);
		}
		else{
			occupants.add(newOccupant);
			setDebugColor(newOccupant,false);
		}
		updateDebugCubeColor();
	}

	public float computeFriction(Ground ground) {
		//TODO maybe better decreasing function for friction
		//if(ground==null)return 0;
		 //in percent
		float decreaseInPercent = Math.min(frictionDecreasePerTouch*touchCounter, maxFrictionDecrease);
		float friction = ground.getFriction()*(1-decreaseInPercent);
		return friction;
	}

	/**
	 * Refreshes current debug-color (sum of debugColors of all ModelObjects on GridField) 
	 * @param mo
	 */
	private void setDebugColor(ModelObject mo, boolean remove) {
		if(Controller.DEBUG_MODE && mo != null){
			//			Log.w(LOG_TAG,"oldColor: "+actDebugColor+" "+((remove?"substract " : " add ")+mo.getDebugColor()));
			float factor = remove? -1:1;
			actDebugColor = actDebugColor.add(mo.getDebugColor().multiply(factor));	
			//			Log.w(LOG_TAG,"new Color: "+actDebugColor);
		}
	}

	/**
	 * Updates color of cube
	 */
	private void updateDebugCubeColor(){
		if(Controller.DEBUG_MODE){
			float factor= occupants.size()+(grounds != null ? grounds.size()-numGroundObstacles : 0);
			//		Log.w(LOG_TAG, "size occs: "+occupants.size()+" ,groundFact: "+((ground==null&&(ground instanceof Obstacle))?0:1));
			debugCube.setColor(actDebugColor.divide((factor==0)?1:factor));
			debugCube.computeNormalsAndRenderStructures();
			//		Log.w(LOG_TAG, "debugColor: "+actDebugColor.divide((factor==0)?1:factor));
		}
	}

	/**
	 * setsGround
	 * @param newGround
	 */
	private void addGround(Ground newGround) {
		grounds.add(newGround);
		//		if(ground==null||ModelObject.getDistance(newGround, ground)>0){
		//			if(ground instanceof Obstacle) occupants.remove(ground);
		if(newGround instanceof Obstacle){
			occupants.add(newGround);
			numGroundObstacles++;
		}
		//			this.ground=newGround;
		//		}
		//		else Log.w(LOG_TAG, "Ground "+newGround+" was not set because already set Ground "+ground+" lays higher "+newGround.getGamePhase()+">"+ground.getGamePhase());
	}


	/**Removes an occupant
	 * @param occupant
	 */
	public void removeOccupant(ModelObject occupant){
		occupants.remove(occupant);
		occupants.trimToSize();
		if(occupant instanceof Ground){
			grounds.remove(occupant);
			if(occupant instanceof Obstacle) numGroundObstacles--;
		}

		setDebugColor(occupant, true);
		updateDebugCubeColor();
		//		if(occupant instanceof Pinguin) touchCounter = 0;
	}

	public MyVector getCenter(){
		return center;
	}


	//Methods for Debugging

	public TriangleMesh getDebugCube() {
		// TODO Auto-generated method stub
		return debugCube;
	}

	public void noticeTouched(){
		if(Controller.gamePhase==Controller.GAMEPHASE_FINISH||Controller.gamePhase==Controller.GAMEPHASE_RINK){
			if(!isOccByPinguin())touchCounter++;
		}
		if(Controller.DEBUG_MODE){
			Ground highestGround = getHighestGround();
			float compFriction=computeFriction(highestGround);
			//			Log.w(LOG_TAG, "GF-friction "+compFriction);
			float difference=(highestGround.getFriction()-compFriction)/highestGround.getFriction();
			//			Log.w(LOG_TAG, "Diff-friction "+difference+ "Ground: "+ground);
			MyVector dc=touchedDebugColor.multiply(1-difference);
			debugCube.setColor(dc);
		}
	}
	public void noticeReleased(){
		if(Controller.DEBUG_MODE){
			updateDebugCubeColor();
		}
	}

	/**
	 * All moveable Objects which occupy this GridField
	 * @return 
	 */
	public Vector<Moveable> getMoveableObstacles() {
		Vector<Moveable> res=new Vector<Moveable>();
		for(ModelObject mo:occupants){
			if(mo instanceof Obstacle&&mo instanceof Moveable){
				res.add((Moveable) mo);
			}
		}
		return res;
	}

	//	public Ground getGround(){
	//		return ground;
	//	}

	public ArrayList<Ground> getGrounds(){
		return grounds;
	}

	public boolean isOccByPinguin(){
		boolean res=false;
		for(ModelObject m:occupants){
			if(m instanceof Pinguin){
				res= true;
				break;
			}
		}
		return res;
	}

	private Ground getHighestGround(){
		float maxHeight = Float.NEGATIVE_INFINITY, groundHeight;
		Ground highestGround = null;
		for(Ground gr : grounds){
			groundHeight = gr.getCenter().get(1) + gr.node.outerSize.get(1)/2; 
			if (groundHeight > maxHeight){
				maxHeight = groundHeight;
				highestGround = gr;
			}
		}
		return highestGround;
	}

	public void destroy() {
		this.center=null;
		this.debugCube=null;
		this.grounds.clear();
		this.occupants.clear();
		//		this.controller=null;
	}

}
